package com.cambright.lotusroot
{
	import com.cambright.Enumerator;
	
	import flash.utils.clearInterval;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	import flash.utils.setInterval;
	
	internal class $Module
	{
		static private const _INSTALLER:$Module = new $Module();
		static public function get INSTALLER():$Module
		{
			return _INSTALLER;
		}
		
		private var _initializedVec:Vector.<Class> = new Vector.<Class>();
		private var _qnameVec:Vector.<String> = new Vector.<String>();
		
		private var _initialRequirePath:Vector.<Class>;
		
		private var _currentInfo:InitialInfo;
		private var _intervalID:int;
		
		public function add(qname:String):void
		{
			if(_qnameVec.indexOf(qname) == -1)
				_qnameVec.push(qname);
		}
		
		public function launch():void
		{
			_initialRequirePath = new Vector.<Class>();
			_initializedVec
			
			var rootInitialInfo:InitialInfo = new InitialInfo();
			_currentInfo = rootInitialInfo;
			
			_initialRequirePath.push(null);
			
			if(_qnameVec.length > 0)
			{
				rootInitialInfo.require = new Enumerator(_qnameVec);
				_buildRequire();
			}
			else
			{
				_intervalID = setInterval(_onInitialized, 1000, null);
			}
		}
		
		private function _build():void
		{
			var definition:Class = _currentInfo.self;
			
			if(definition == null)
				throw Error("");
			if(_initializedVec.indexOf(definition) > -1)
				_back();
			if(_initialRequirePath.indexOf(definition) > -1)
				throw Error("");
			
			var module:Module = new definition();
			_currentInfo.module = module;
			var require:Vector.<Class> = module.require;
			if(require != null)
			{
				_currentInfo.require = new Enumerator(require);
				_buildRequire();
			}
			else _initialize();
		}
		
		private function _buildRequire():void
		{
			if(_currentInfo.require.next())
			{
				_initialRequirePath.push(_currentInfo.self);
				var childInfo:InitialInfo = new InitialInfo();
				childInfo.parent = _currentInfo;
				childInfo.self = getDefinitionByName(_currentInfo.require.current) as Class;
				_currentInfo = childInfo;
				_build();
			}
			else if(_currentInfo.parent == null)
				_complete();
			else _initialize();
		}
		
		private function _initialize():void
		{
			
			var module:Module = _currentInfo.module;
			if(module.asynInitial)
				module.addEventListener(Notice.TYPE_MODULE_INITIALIZED, _onInitialized);
			else
				_intervalID = setInterval(_onInitialized, 30, null);
			
			_inject();
		}
		
		private function _onInitialized(event:Notice):void
		{
			clearInterval(_intervalID);
			
			if(_currentInfo.self != null)
			{
				trace("[MODULE] " + getQualifiedClassName(_currentInfo.self) + " has been install");
				_initializedVec.push(_currentInfo.self);
				Application.$CURRENT.$moduleVec.push(_currentInfo.module);
				_dispatchMain();
			}
			
			_back();
		}
		
		private function _back():void
		{
			var info:InitialInfo = _currentInfo;
			_currentInfo = _currentInfo.parent;
			info.clear();
			
			_initialRequirePath.removeAt(_initialRequirePath.length - 1);
			
			_buildRequire();
		}
		
		private function _inject():void
		{
			var module:Module = _currentInfo.module;
			var commandMap:CommandMap = module.commandMap;
			if(commandMap != null)
			{
				for(var command:Class in commandMap)
				{
					var handler:Class = commandMap.value(command);
					Application.$CURRENT.$commandMap.$add(module, command, handler);
				}
			}
			
			module.$initialize();
		}
		
		private function _dispatchMain():void
		{
			var command:Class = _currentInfo.module.mainCommand;
			if(command != null)
			{
				var main:Command = new command();
				main.dispatch();
			}
		}
		
		private function _complete():void
		{
			_initialRequirePath = null;
			
			Application.$CURRENT.$step();
		}
	}
}
import com.cambright.Enumerator;
import com.cambright.lotusroot.Module;

class InitialInfo
{
	public var parent:InitialInfo;
	public var require:Enumerator;
	public var self:Class;
	public var module:Module;
	
	public function clear():void
	{
		parent = null;
		if(require != null)
			require.dispose();
		require = null;
		self = null;
		module = null;
	}
}
